package com.timber.util;

import java.awt.Component;
import java.awt.Container;
import java.awt.Toolkit;
import java.awt.Window;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.TimeZone;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;


/**
 *	a set of static methods to print error, logs, debug messages, etc
 * debug messages are printed when debugState is true
 *
 */
public class Util
{
  public  static final String		STR_DEBUG  = "DEBUG";
  public  static final int          TS_SECONDS = 1;
  public  static final int          TS_MILLISECONDS = 2;

  private static boolean		    debugState = false;
  private static SimpleDateFormat	sdf = new SimpleDateFormat( "dd-MMM-yyyy HH:mm:ss - " );
  private static SimpleDateFormat	sdfMS = new SimpleDateFormat( "dd-MMM-yyyy HH:mm:ss.SSS - " );
  private static SimpleDateFormat   defaultSDF = sdf;

  public Util ()
  {
  }

  public static void setDebug( boolean state )
  {
    debugState = state;
  }

  /**
   * get source name, class name, from refObj
   *
   * @param refObj
   * @param message
   */
  public static void debug( Object refObj, String message )
  {
    final String name = objToName(refObj);
    debug( name + ": " + message );
  }

  public static void debug( String src, String message )
  {
    debug( src + ": " + message );
  }

  public static void debug( String message )
  {
    if( debugState )
    {
      stdout( message );
    }
  }

/**
 * log message, source + message
 * @param src
 * @param message
 */
  public static void log( String src, String message )
  {
    log( src + ": " + message );
  }

  public static void log( String message )
  {
    stdout( message );
  }

  public static void log( Object refObj, String message )
  {
      final String name = objToName(refObj);
      log( name + ": " + message );
  }

    private static String objToName(Object refObj) {
        final String fullName = refObj.getClass().getName();
        final int ind = fullName.lastIndexOf('.');
        final String name = fullName.substring(ind + 1);
        return name;
    }

/**
 * write to stderr, source + message
 * @param src
 * @param message
 */
    public static void error( String src, String message )
  {
    error( src, message, null );
  }

  public static void error( String message )
  {
    error( message, (String)null );
  }

  public static void error( Exception e )
  {
    error( null, e );
  }

  public static void error( String message, Exception e )
  {
    error( null, message, e );
  }

  public static void error( Object refObj, String message, Exception e )
  {
    final String name = objToName(refObj);
    error( name, message, e );
  }
/**
 * write to stderr, print stack trace and e != null
 *
 * @param src
 * @param message
 * @param e
 */
  public static void error( String src, String message, Exception e )
  {
    src = ( src == null ? "" : src + ": " );
    message = ( message == null ? "" : message );
    src = src + message;

    if( src.length() > 0 )
      stderr( src );

    if( e != null )
    {
      stderr( e.getMessage() );
      e.printStackTrace();
      if( Global.getLogWriter() != null )
	e.printStackTrace(Global.getLogWriter());
    }
  }


  public static boolean debugMode()
  {
    return debugState;
  }

  public static void setTSFormat(int i)
  {
    switch(i) {
        case (TS_SECONDS):
             defaultSDF = sdf;
             break;
        case (TS_MILLISECONDS):
             defaultSDF = sdfMS;
             break;
    }
  }

  public static TimeZone getLocationTZ()
  {
    return getLocationTZ( Global.getLocation() );
  }

  /**
   * get time zone for specific location
   *
   * @param aLocation
   * @return
   */
  public static TimeZone getLocationTZ( String aLocation )
  {
    String locID = null;
    if( "LN".equals( aLocation ) )      { locID = "Europe/London"; }
    else if( "NY".equals( aLocation ) ) { locID = "America/New_York"; }
    else if( "TK".equals( aLocation ) ) { locID = "Asia/Tokyo"; }
    else if( "FT".equals( aLocation ) ) { locID = "Europe/Berlin"; }
    else if( "SY".equals( aLocation ) ) { locID = "Australia/Sydney"; }
    else if( "TR".equals( aLocation ) ) { locID = "America/Toronto"; }

    if( locID != null ) return SimpleTimeZone.getTimeZone( locID );
    else Util.log( "Global.SetLocationTZ(): No preset TimeZone for location " +
		   ( aLocation != null ? aLocation : "null" ) );
    return TimeZone.getDefault();
  }

  /**
   *  Print current stack trace
   *  for debuging purposes
   */
  public static void printStackTrace()
  {
    new Exception( "Stack Trace" ).printStackTrace();
  }


  public static void trace( String str )
  {
  }


  /**
   *    Helper string function to pad a string value with spaces to a
   *    fixed width.
   *
   *    @param value string to be padded
   *    @param totalWidth width of padded string
   */

  public static String paddedValue( String value, int totalWidth )
  {
    return paddedValue(value, totalWidth, true);
  }

  public static String paddedValue( String value, int totalWidth, boolean padRight)
  {
    StringBuffer buffer = new StringBuffer( "" );
    int length = value.length();
    if( length >= totalWidth )
    {
      return value.substring( 0, totalWidth - 1 );
    }
    else
    {
      for( int i = 0; i < ( totalWidth - length ); ++i )
      {
        buffer.append( ' ' );
      }
      if (padRight == true)
        return value + buffer.toString();
      else
        return buffer.toString() + value;
    }
  }



  /**
   *  Debug object for getting elapsed time and memory usage.
   *
   *  use startDebug() and stopDebug()
   *
   */
  public static DebugInfo startDebug( String descriptor )
  {
    if( debugMode() )
    {
      DebugInfo info = new DebugInfo( descriptor,
				      System.currentTimeMillis(),
				      Runtime.getRuntime().freeMemory() );
      return info;
    }
    else
      return null;
  }

  public static void stopDebug( DebugInfo info )
  {
    if( debugMode() )
    {
      long stopTime = System.currentTimeMillis();
      long fm = Runtime.getRuntime().freeMemory();

      Util.debug( "DEBUG INFO: " +
		  info.descriptor +
		  ": " +
		  ( stopTime - info.startTime ) +
		  " ms " +
		  ( info.initialFreeMemory - fm / 1000.0 ) +
		  " KB" );
    }
    else
      return;
  }

  /**
   *	print to stdout and Global.getLogWriter()
   */
  private static void stdout( String str )
  {
    System.out.println( timestamp() + str );
    if( Global.getLogWriter() != null )
      Global.getLogWriter().println( timestamp() + str );
  }


  /**
   *	print to stderr and Global.getLogWriter()
   */
  private static void stderr( String str )
  {
    System.err.println( timestamp() + str );
    if( Global.getLogWriter() != null )
      Global.getLogWriter().println( timestamp() + str );
  }

  public static String timestamp()
  {
    return defaultSDF.format( new Date() );
  }

    public static void beep()
    {
	Toolkit.getDefaultToolkit().beep();
    }


  /**
   * Creates and returns a new modal JDialog appropriately parented given an
   * arbitrary parent component
   *
   * @param parentComponent determines the frame in which the dialog
   *		is displayed.
   * @param title	the title string for the dialog
   * @return a new JDialog
   */
  public static JDialog createDialog( Component parentComponent, String title )
  {
    final JDialog dialog;
    Window window = Util.getWindowForComponent( parentComponent );
    if( window instanceof JFrame )
    {
      dialog = new JDialog( ( JFrame )window, title, true );
    }
    else
    {
      dialog = new JDialog( ( JDialog )window, title, true );
    }

    return dialog;
  }

  /**
   * Returns the specified component's toplevel JFrame or
   * JDialog.
   *
   * @param parentComponent the Component to check for a
   *		JFrame or JDialog
   * @return the JFrame or JDialog that
   *		contains the component, or the default
   *         	frame if the component is null,
   *		or does not have a valid
   *         	JFrame or JDialog parent
   */
  public static Window getWindowForComponent( Component parentComponent )
  {
    if( parentComponent == null )
      return JOptionPane.getRootFrame();

    if( parentComponent instanceof JFrame || parentComponent instanceof JDialog )
      return ( Window )parentComponent;

    return Util.getWindowForComponent( parentComponent.getParent() );
  }

  /**
   * Returns the specified component's toplevel glassPane. This method
   * recurses up the component parent tree looking for a JFrame or
   * JDialog. if ine of these is found then the respective glassPane
   * is returned. If neither a JDialog or JFrame is found then null
   * is returned.
   *
   * @param parentComponent the Component to check for a glassPane
   *
   * @return the glassPane Component or null if no glassPane is found.
   *
   */
  public static Component getGlassPaneForComponent( Component parentComponent )
  {
    Window window = getWindowForComponent( parentComponent );
    if( window instanceof JFrame )
      return ( ( JFrame )window ).getGlassPane();
    else if( window instanceof JDialog )
      return ( ( JDialog )window ).getGlassPane();
    else
      return null;
  }

  /**
   * recursively call setEnabled() method on all children of the Container
   * @param comp Container
   * @param b boolean
   */
  public static void setAllEnabled(Container comp, boolean b) {
      Component guiComp[] = comp.getComponents();
      for (int i = 0; i < guiComp.length; i++) {
          if (((Container) guiComp[i]).getComponentCount() > 1)
              setAllEnabled((Container) guiComp[i], b);
          guiComp[i].setEnabled(b);
      }
  }
}
